package middleware

import (
	"context"
	"github.com/dgrijalva/jwt-go"
	"github.com/gin-gonic/gin"
	"github.com/pkg/errors"
	"net/http"
	"wx-uniapp-go/internal/common"
	"wx-uniapp-go/internal/domain/errcode"
	"wx-uniapp-go/internal/logic/token_logic"
	"wx-uniapp-go/internal/logic/user_logic"
	models "wx-uniapp-go/internal/repository/models"
	"wx-uniapp-go/internal/server/ctxop"
	"wx-uniapp-go/internal/server/echo"
	"wx-uniapp-go/third_party/cdslog"
	"wx-uniapp-go/third_party/errs"
)

const (
	Header_Authorization = "Authorization"
	Cookie_Authorization = "Authorization"
)

func CheckLogin() gin.HandlerFunc {
	return func(c *gin.Context) {
		ctx := echo.TraceCtx(c)
		tokenStr, _ := c.Cookie(Cookie_Authorization)
		if tokenStr == "" {
			tokenStr = c.GetHeader(Header_Authorization)
		}
		if tokenStr != "" {
			tokenData, err := token_logic.NewTokenClient(ctx).CheckCommonToken(tokenStr)
			if err == nil {
				if uidStr, ok := tokenData.Id.(string); ok {
					uid := models.UidDecode(uidStr)
					uInfo, err := models.UserGetOneById(ctx, uid)
					// 封禁校验
					if uInfo.Status == user_logic.UserStatusLock {
						echo.Fail(c).Error(errcode.UserLock)
						c.AbortWithStatus(http.StatusOK)
						return
					}
					c.Request = c.Request.WithContext(
						setLoginInfo(c.Request.Context(), uInfo))
					if err == nil {
						go func() {
							defer common.DontPanic()
						}()

						//uid,uInfo存入上下文会话
						echo.SetUserInfo(c, uInfo)
						c.Next()
						return
					} else {
						cdslog.W(ctx).Error(err.Error())
						echo.Fail(c).Error(errcode.UserNotExist)
						c.AbortWithStatus(http.StatusOK)
						return
					}
				}
			} else {

				if errors.Is(err, token_logic.ErrOtherDeviceLogin) {
					echo.DirectRespErr(c, err)
					c.Abort()
					return
				}

				var e *jwt.ValidationError
				if errors.As(err, &e) {
					if e.Errors == jwt.ValidationErrorExpired {
						echo.DirectRespErr(c, errs.NewBusinessErr(errcode.Unauthorized.Code, "登录已过期，请重新登录!"))
						c.Abort()
						return
					}
				}
				cdslog.W(c).Error(err.Error())
				echo.DirectRespErr(c, errs.NewBusinessErr(errcode.Unauthorized.Code, "token无效"))
				c.Abort()
				return
			}
		}
		echo.Fail(c).Error(errcode.Unauthorized)
		c.AbortWithStatus(http.StatusOK)
		return
	}
}

func setLoginInfo(ctx context.Context, user *models.User) context.Context {

	return ctxop.UserSet(ctx, user)
}

// GetUserInfo 通过token获取user信息
// 已经被CheckLogin()认证过的接口就不要再调这个方法了，调这个 echo.GetUserInfo(ctx)
// Deprecated: 已废弃，请使用ctxop.UserInfoGet(ctx)
func GetUserInfo(c *gin.Context) (*models.User, error) {
	ctx := echo.TraceCtx(c)
	tokenStr, _ := c.Cookie(Cookie_Authorization)
	if tokenStr == "" {
		tokenStr = c.GetHeader(Header_Authorization)
	}
	if tokenStr != "" {
		tokenData, err := token_logic.NewTokenClient(ctx).CheckCommonToken(tokenStr)
		if err == nil {
			if uidStr, ok := tokenData.Id.(string); ok {
				uid := models.UidDecode(uidStr)
				return models.UserGetOneById(ctx, uid)
			}
		}
	}
	return &models.User{}, nil
}
